using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Nodes;
using DevExpress.XtraTreeList.ViewInfo;
using SVNClean.Modules;
using SVNClean.Properties;
using SVNClean.Windows;

namespace SVNClean.Controls {
    public partial class ctrl_Explorer : DevExpress.XtraEditors.XtraUserControl {
        /// <summary>
        /// Initializes a new instance of the <see cref="ExplorerNew"/> class.
        /// </summary>
        public ctrl_Explorer() {
            InitializeComponent();
            colName.Caption = Environment.MachineName;
            IgnorePaths = new List<string>();
        }

        #region "[rgn] Form Properties "
        public event EventHandler<DirectoryEventArgs> DirectoryChanged;
        public delegate void CallBack();

        public bool LoadDrives { get; set; }
        public int LastCount { get; set; }

        public List<string> IgnorePaths { get; set; }

        private DirectoryInfo currentDirectory;
        public DirectoryInfo CurrentDirectory {
            get { return currentDirectory; }
            set {
                currentDirectory = value;
                if (DirectoryChanged != null)
                    DirectoryChanged(this, new DirectoryEventArgs { Directory = value });
            }
        }
        #endregion

        #region "[rgn] TreeList Events "
        private void treeList1_VirtualTreeGetCellValue(object sender, DevExpress.XtraTreeList.VirtualTreeGetCellValueInfo e) {
            DirectoryInfo di = new DirectoryInfo((string)e.Node);
            if (e.Column == colName) {
                e.CellData = new PathNode { Directory = di };
            }
        }
        private void treeList1_VirtualTreeGetChildNodes(object sender, DevExpress.XtraTreeList.VirtualTreeGetChildNodesInfo e) {
            Cursor current = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
            if (!LoadDrives) {
                string[] roots = Directory.GetLogicalDrives();
                e.Children = (from root in roots where !IgnorePaths.Contains(root) select root).ToList();
                LoadDrives = true;
            }
            else {
                try {
                    string path = (string)e.Node;
                    if (!IgnorePaths.Contains(path) && Directory.Exists(path)) {
                        var contents = new List<string>();

                        //Add child directories.
                        string[] dirs = Directory.GetDirectories(path);
                        foreach (var dir in dirs) {
                            if (dir.IsSvnDirectory()) {
                                contents.Add(dir);
                            }
                            else {
                                if (!AppConfiguration.Current.ShowHiddenFolders) {
                                    var dirinfo = new DirectoryInfo(dir);
                                    if ((dirinfo.Attributes & FileAttributes.Hidden) <= 0)
                                        contents.Add(dir);
                                }
                                else { contents.Add(dir); }
                            }
                        }
                        //Add child files.
                        string[] files = Directory.GetFiles(path);
                        if (AppConfiguration.Current.ShowFiles) {
                            foreach (var file in files) {
                                if (!AppConfiguration.Current.ShowHiddenFiles) {
                                    var fileinfo = new FileInfo(file);
                                    if ((fileinfo.Attributes & FileAttributes.Hidden) <= 0) {
                                        contents.Add(file);
                                    }
                                }
                                else { contents.Add(file); }
                            }
                        }
                        string[] arr = contents.ToArray();
                        e.Children = arr;
                    }
                    else e.Children = new object[] { };
                }
                catch { e.Children = new object[] { }; }
            }
            Cursor.Current = current;
        }
        private void treeList1_GetStateImage(object sender, DevExpress.XtraTreeList.GetStateImageEventArgs e) {
            try {
                var pathNode = (PathNode)e.Node.GetValue(0);
                if (pathNode == null) return;
                if (IgnorePaths.Contains(pathNode.Directory.FullName)) { e.NodeImageIndex = 9; return; }

                if (pathNode.Type == PathContent.File) {
                    if (!string.IsNullOrEmpty(pathNode.File.Extension)) {
                        if (!imgSystem.Images.ContainsKey(pathNode.File.Extension)) {
                            imgSystem.Images.Add(pathNode.File.Extension, Win32.GetFileIcon(pathNode.File.FullName, Win32.IconSize.Small));
                        }
                        e.NodeImageIndex = imgSystem.Images.IndexOfKey(pathNode.File.Extension);
                        return;
                    }
                }
                else {

                    if (pathNode.IsSVNDirectory) {
                        e.NodeImageIndex = 1;
                        return;
                    }
                    else if (pathNode.IsSVNContainer) {
                        e.NodeImageIndex = 0;
                        return;
                    }
                    if (!imgSystem.Images.ContainsKey(pathNode.Directory.FullName)) {
                        var nodeIcon = Win32.GetFileIcon(pathNode.Directory.FullName, Win32.IconSize.Small);
                        if (nodeIcon != null) { imgSystem.Images.Add(pathNode.Directory.FullName, nodeIcon); }

                    }
                    e.NodeImageIndex = imgSystem.Images.IndexOfKey(pathNode.Directory.FullName);
                    return;
                }
                e.NodeImageIndex = 2;
            }
            catch { e.NodeImageIndex = 3; }
        }
        private void treeList1_CustomDrawNodeCell(object sender, DevExpress.XtraTreeList.CustomDrawNodeCellEventArgs e) {
            if ((e.Node.GetValue(0) as PathNode) != null) {
                var pathNode = ((PathNode)e.Node.GetValue(0));
                if (pathNode.IsSVNDirectory || pathNode.IsSVNContainer) {
                    e.Appearance.ForeColor = AppConfiguration.Current.SVNPathForeColor;
                    e.Appearance.BackColor = AppConfiguration.Current.SVNPathBackColor;
                    return;
                }
                else if (e.Node.ParentNode != null) {
                    var parentPathNode = ((PathNode)e.Node.ParentNode.GetValue(0));
                    if (parentPathNode.IsSVNDirectory) {
                        e.Appearance.ForeColor = AppConfiguration.Current.SVNPathForeColor;
                        e.Appearance.BackColor = AppConfiguration.Current.SVNPathBackColor;
                        return;
                    }
                }
            }
        }
        private void treeList1_FocusedNodeChanged(object sender, DevExpress.XtraTreeList.FocusedNodeChangedEventArgs e) {
            CurrentDirectory = ((PathNode)e.Node.GetValue(0)).Directory;
            try {
                txtPath.Text = CurrentDirectory.GetDirectoryPath();
            }
            catch (Exception ex) {
                IgnorePaths.Add(currentDirectory.FullName);
                AppConfiguration.Current.LastCleanedDirectory = string.Empty;
                AppConfiguration.Current.SaveData();
                MessageBox.Show("Error loading last directory:\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        private void treeList1_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.F5) {
                var dir = CurrentDirectory;
                InitData();
                SelectDirectory(dir.FullName);
            }
        }
        #endregion

        #region "[rgn] Controls Events "
        private void txtPath_Properties_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e) {
            txtPath.ClosePopup();
            if (e.Button.Kind == DevExpress.XtraEditors.Controls.ButtonPredefines.Glyph) {
                string dir = string.Empty;
                if (CurrentDirectory != null) dir = CurrentDirectory.FullName;
                else dir = Directory.GetLogicalDrives()[0];
                InitData();
                SelectDirectory(dir);
            }
        }
        private void txtPath_KeyUp(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up) {
                txtPath.SelectionLength = 0;
            }
            else if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right) {
                return;
            }
            else if (e.KeyCode == Keys.Home || e.KeyCode == Keys.Shift ||
            e.KeyCode == Keys.End || e.KeyCode == Keys.Delete ||
            e.KeyCode == Keys.Back || e.KeyCode == Keys.Control) {
                return;
            }
            else if (e.KeyCode == Keys.Enter) {
                SelectDirectory(txtPath.Text);
            }
            else if (e.KeyCode == Keys.F5) {
                InitData();
            }
            else {
                ShowSubDirectories();
            }
            txtPath.SelectionStart = txtPath.Text.Length;
        }
        private void txtPath_SelectedValueChanged(object sender, EventArgs e) {
            txtPath.SelectionStart = txtPath.Text.Length;
        }
        private void txtPath_Leave(object sender, EventArgs e) {
            if (CurrentDirectory != null) {
                if (CurrentDirectory.FullName.ToLower() != txtPath.Text) {
                    SelectDirectory(txtPath.Text);
                }
            }
            txtPath.SelectionStart = 0;
        }
        private void txtPath_Click(object sender, EventArgs e) {
            ShowSubDirectories();
        }
        private void ctrl_Explorer_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.F5)
                InitData();
        }
        private void countSVNReferencesToolStripMenuItem_Click(object sender, EventArgs e) {
            CountSVNReferences(((PathNode)treeList1.FocusedNode.GetValue(0)).Directory);
        }
        private void cleanThisDirectoryToolStripMenuItem_Click(object sender, EventArgs e) {
            this.ParentForm.Hide();
            var frmClean = new frm_Clean { DirectoryToClean = CurrentDirectory };
            frmClean.ShowDialog();

            string dir = CurrentDirectory.FullName;
            InitData();
            SelectDirectory(dir);
            this.ParentForm.Show();
        }
        private void cancelToolStripMenuItem_Click(object sender, EventArgs e) {
            contextMenuStrip1.Close();
        }
        private void treeList1_MouseDown(object sender, MouseEventArgs e) {
            var hitinfo = treeList1.CalcHitInfo(new System.Drawing.Point(e.X, e.Y)); ;
            if (hitinfo.Node != null) {
                var path = ((PathNode)hitinfo.Node.GetValue(0));
                if (!IgnorePaths.Contains(path.Directory.FullName)) {
                    treeList1.SetFocusedNode(hitinfo.Node);
                    txtPath.Text = ((PathNode)hitinfo.Node.GetValue(0)).Directory.GetDirectoryPath();
                    CurrentDirectory = ((PathNode)hitinfo.Node.GetValue(0)).Directory.GetDirectoryPathInfo();
                }
            }
        }
        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) {
            var hitinfo = treeList1.CalcHitInfo(Cursor.Position);
            if (hitinfo.Node != null)
                treeList1.SetFocusedNode(hitinfo.Node);

            if (treeList1.FocusedNode == null)
                e.Cancel = true;
            else {
                cleanThisDirectoryToolStripMenuItem.Text = "Clean \"" + ((PathNode)treeList1.FocusedNode.GetValue(0)).Directory.FullName + "\"";
            }
        }
        private void toolTipController1_GetActiveObjectInfo(object sender, DevExpress.Utils.ToolTipControllerGetActiveObjectInfoEventArgs e) {

            if (e.SelectedControl is DevExpress.XtraTreeList.TreeList) {
                var tree = (TreeList)e.SelectedControl;
                var hit = tree.CalcHitInfo(e.ControlMousePosition);
                if (hit.HitInfoType == HitInfoType.Cell) {

                    #region "[rgn] SuperTip Setup "
                    var superToolTip1 = new DevExpress.Utils.SuperToolTip();
                    var toolTipTitleItem1 = new DevExpress.Utils.ToolTipTitleItem();
                    var toolTipItem1 = new DevExpress.Utils.ToolTipItem();
                    var toolTipSeparatorItem1 = new DevExpress.Utils.ToolTipSeparatorItem();
                    var toolTipTitleItem2 = new DevExpress.Utils.ToolTipTitleItem();
                    toolTipItem1.LeftIndent = 6;
                    toolTipTitleItem2.LeftIndent = 6;
                    toolTipTitleItem1.Appearance.Options.UseImage = true;
                    #endregion

                    var dir = ((PathNode)hit.Node.GetValue(0));
                    try {
                        if (IgnorePaths.Contains(dir.Directory.FullName)) { return; }
                        if (dir.Type == PathContent.File) {

                            toolTipTitleItem1.Text = dir.File.Name;
                            toolTipItem1.Text = "Path: " + dir.Directory.FullName + Environment.NewLine;
                            toolTipItem1.Text += "Length: " + (dir.File.Length / 1024) + " Kb" + Environment.NewLine;
                            toolTipItem1.Text += "Created: " + dir.File.CreationTime.ToLongDateString() + Environment.NewLine;
                            toolTipItem1.Text += "Modified: " + dir.File.LastWriteTime.ToLongDateString() + Environment.NewLine;
                            toolTipTitleItem2.Text = Win32.GetFileInfo(dir.Directory.FullName).szTypeName;
                        }
                        else {
                            if (dir.IsSVNDirectory || dir.IsSVNContainer) {
                                toolTipTitleItem1.Text = dir.Name;
                                toolTipTitleItem1.Appearance.ForeColor = AppConfiguration.Current.SVNPathForeColor;
                                toolTipItem1.Text = "Path: " + dir.Directory.FullName + Environment.NewLine;
                                toolTipTitleItem2.Text = "SVN Cache Directory";
                            }
                            else {
                                toolTipTitleItem1.Text = dir.Directory.Name;
                                toolTipItem1.Text = "Path: " + dir.Directory.FullName + Environment.NewLine;
                                toolTipTitleItem2.Text = Win32.GetFileInfo(dir.Directory.FullName).szTypeName;
                            }
                            toolTipItem1.Text += "Created: " + dir.Directory.CreationTime.ToLongDateString() + Environment.NewLine;
                            toolTipItem1.Text += "Modified: " + dir.Directory.LastWriteTime.ToLongDateString() + Environment.NewLine;
                        }

                        #region "[rgn] Image Tip Setup "
                        int index = -1;
                        if (dir.Type == PathContent.File) {
                            if (!string.IsNullOrEmpty(dir.File.Extension)) {
                                if (!imgToolTip.Images.ContainsKey(dir.File.Extension)) {
                                    imgToolTip.Images.Add(dir.File.Extension, Win32.GetFileIcon(dir.File.FullName, Win32.IconSize.Large));
                                }
                                index = imgToolTip.Images.IndexOfKey(dir.File.Extension);
                                toolTipTitleItem1.Image = imgToolTip.Images[index];
                                toolTipTitleItem1.Appearance.Image = imgToolTip.Images[index];
                            }
                        }
                        else {
                            if (dir.IsSVNDirectory || dir.IsSVNContainer) {
                                toolTipTitleItem1.Image = imgToolTip.Images[0];
                                toolTipTitleItem1.Appearance.Image = imgToolTip.Images[0];
                            }
                            else {
                                if (!imgToolTip.Images.ContainsKey(dir.Directory.FullName)) {
                                    imgToolTip.Images.Add(dir.Directory.FullName, Win32.GetFileIcon(dir.Directory.FullName, Win32.IconSize.Large));
                                }
                                index = imgToolTip.Images.IndexOfKey(dir.Directory.FullName);
                                toolTipTitleItem1.Image = imgToolTip.Images[index];
                                toolTipTitleItem1.Appearance.Image = imgToolTip.Images[index];
                            }
                        }
                        #endregion

                        superToolTip1.Items.Add(toolTipTitleItem1);
                        superToolTip1.Items.Add(toolTipItem1);
                        superToolTip1.Items.Add(toolTipSeparatorItem1);
                        superToolTip1.Items.Add(toolTipTitleItem2);

                        object cellInfo = new TreeListCellToolTipInfo(hit.Node, hit.Column, null);
                        e.Info = new DevExpress.Utils.ToolTipControlInfo(cellInfo, "");
                        e.Info.SuperTip = superToolTip1;

                    }
                    catch (Exception ex) {
                        IgnorePaths.Add(dir.Directory.FullName);
                        AppConfiguration.Current.LastCleanedDirectory = string.Empty;
                        AppConfiguration.Current.SaveData();
                        MessageBox.Show("Error loading last directory:\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }
        #endregion

        /// <summary>
        /// Inits the data.
        /// </summary>
        public void InitData() {
            this.BeginInvoke(new CallBack(delegate() {
                LoadDrives = false;
                treeList1.DataSource = new object();
            }));
        }

        /// <summary>
        /// Counts the SVN references.
        /// </summary>
        /// <param name="dir">The dir.</param>
        public static void CountSVNReferences(DirectoryInfo dir) {
            new Callback(delegate() {
                var SvnDirectories = new List<DirectoryInfo>();
                var SVNReferences = 0;
                foreach (string svn in SvnDirectoryNames.Names) {
                    SvnDirectories.AddRange(dir.GetDirectories(svn, SearchOption.AllDirectories));
                }
                var SvnFiles = new List<FileInfo>();
                foreach (var subdir in SvnDirectories) {
                    SVNReferences += subdir.GetDirectories("*", SearchOption.AllDirectories).Length;
                    SvnFiles.AddRange(subdir.GetFiles("*", SearchOption.AllDirectories));
                }
                SVNReferences += (SvnDirectories.Count + SvnFiles.Count);

                MessageBox.Show(string.Format(
                    Resources.SVNReferences, SVNReferences), Resources.References,
                    MessageBoxButtons.OK, MessageBoxIcon.Information);

            }).BeginInvoke(null, null);
        }

        /// <summary>
        /// Selects the last directory.
        /// </summary>
        public void SelectLastDirectory() {
            try {
                this.BeginInvoke(new CallBack(delegate() {
                    if (AppConfiguration.Current.LastCleanedDirectory != "") {
                        SelectDirectory(AppConfiguration.Current.LastCleanedDirectory);
                    }
                    else { SelectDirectory(Directory.GetLogicalDrives()[0]); }
                }));
            }
            catch (Exception ex) {
                AppConfiguration.Current.LastCleanedDirectory = string.Empty;
                AppConfiguration.Current.SaveData();
                MessageBox.Show("Error loading last directory:\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Select's the last directory node in specified path.
        /// </summary>
        /// <param name="path">Directory Path.</param>
        public void SelectDirectory(string path) {
            if (!IgnorePaths.Contains(path)) {
                try {
                    if (string.IsNullOrEmpty(path)) return;
                    if (treeList1.Nodes.Count > 0) {
                        var idir = (new DirectoryInfo(path));
                        var dir = idir.GetDirectoryPathInfo();
                        var dirNames = new List<string>();

                        DirectoryInfo nextDir = dir;
                        while (nextDir != null) {
                            dirNames.Add(nextDir.Name);
                            nextDir = nextDir.Parent;
                        }

                        TreeListNodes parentNodes = treeList1.Nodes;
                        TreeListNode lastNode = null;
                        dirNames.Reverse();

                        foreach (var dirName in dirNames) {
                            foreach (TreeListNode node in parentNodes) {
                                if (node.GetValue(0).ToString().ToLower() == dirName.ToLower()) {
                                    node.Expanded = true;
                                    lastNode = node;
                                    parentNodes = node.Nodes;
                                    break;
                                }
                            }
                        }

                        if (lastNode != null) {
                            treeList1.SetFocusedNode(lastNode);
                            lastNode.Selected = true;
                            return;
                        }
                    }
                    //MessageBox.Show("Directory Not Found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                catch (NotSupportedException ex) {
                    IgnorePaths.Add(path);
                    AppConfiguration.Current.LastCleanedDirectory = string.Empty;
                    AppConfiguration.Current.SaveData();
                    MessageBox.Show("Invalid Path:\n" + ex.Message + "\n\nThis path is now ignored!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        /// <summary>
        /// Show sub-directories basead on txtPath value.
        /// </summary>
        public void ShowSubDirectories() {
            try {
                var dir = new DirectoryInfo(txtPath.Text);
                var subDirs = dir.GetDirectories();

                txtPath.Properties.Items.Clear();
                foreach (var subDir in subDirs) {
                    txtPath.Properties.Items.Add(subDir.FullName);
                }
                txtPath.ShowPopup();
            }
            catch (Exception ex) {
                //MessageBox.Show("Directory Not Found!\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
                ex.ToString();
            }
        }

        /// <summary>
        /// Expands the First Node.
        /// </summary>
        public void ExpandFirstNode() {
            if (treeList1.Nodes.Count > 0) {
                treeList1.Nodes[0].Expanded = true;
                treeList1.SetFocusedNode(treeList1.Nodes[0]);
            }
        }

    }
}

